/**
 * 用户信息数据模型
 * @author HalRui
 * @version 1.0.0
 * @description 基于Mongoose的用户信息数据模型，定义用户数据结构、验证规则和操作方法
 * @date 2025年9月27日
 */

const mongoose = require('mongoose');

let bcrypt;
try {
    bcrypt = require('bcryptjs');
} catch (error) {
    // 如果bcryptjs未安装，使用模拟实现
    bcrypt = require('../utils/bcrypt-mock');
}

// 用户ID计数器模型，用于生成自动增长的用户ID
const UserIdCounterSchema = new mongoose.Schema({
    _id: { type: String, required: true },
    sequence_value: { type: Number, default: 0 }
});

const UserIdCounter = mongoose.model('UserIdCounter', UserIdCounterSchema);

const schema =  mongoose.Schema({
    userId: { // 独立的自动增长用户ID，从1开始递增，业务层主键
        type: Number,
        unique: true,
        index: true,
        description: "独立的自动增长用户ID"
    },
    username: { // 用户名，可选，支持中英文、数字、下划线，长度2-20位
        type: String,
        required: false, // 改为可选字段
        unique: true,
        sparse: true, // 允许多个null值，但非null值必须唯一
        trim: true,
        minlength: [2, "用户名至少2位长度"],
        maxlength: [20, "用户名最多20位长度"],
        match: [/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, "用户名只能包含字母、数字、下划线和中文"]
    },
    email : { // 邮箱地址，唯一，严格的格式验证
        type: String,
        required: [true , "邮箱不允许为空"],
        unique : true,
        trim : true,
        match : [/^[A-Za-z0-9_.-]{3,30}@[A-Za-z0-9-]{2,20}(\.[A-Za-z0-9-]{2,20})+$/ , "邮箱格式不合法"]
    },
    password : { // 密码，自动加密存储，至少8位，必须包含字母和数字
        type: String,
        required: [true , "密码不允许为空"],
        trim : true,
        minlength : [8, "密码至少8位长度"],
        match : [/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&.])[A-Za-z\d@$!%*#?&_.]{8,}$/,"密码至少8位，必须包含字母和数字"]
    },
    avatar : { // 头像路径，默认为系统默认头像
        type: String,
        default : "/public/images/default-avatar.png",
    },
    phone : { // 手机号，可选，中国手机号格式验证（1开头，11位）
        type: String,
        trim : true,
        validate: {
            validator: function(phone) {
                // 如果phone为空或null，则验证通过
                if (!phone || phone.trim() === '') {
                    return true;
                }
                // 如果有值，则验证格式
                return /^1[3-9]\d{9}$/.test(phone);
            },
            message: "手机号格式不合法"
        }
    },
    gender: { // 性别，支持male/female/other/secret，默认为secret
        type: String,
        enum: ['male', 'female', 'other', 'secret'],
        default: 'secret'
    },
    birthday: { // 生日，可选，日期范围验证（1900年至今）
        type: Date,
        validate: {
            validator: function(date) {
                // 如果birthday为空或null，则验证通过
                if (!date) {
                    return true;
                }
                // 如果有值，则验证日期范围
                return date <= new Date() && date >= new Date('1900-01-01');
            },
            message: "生日日期不合法"
        }
    },
    nickname: { // 昵称，可选，最大50个字符
        type: String,
        trim: true,
        maxlength: [50, "昵称最多50个字符"]
    },
    userType: { // 用户类型，支持customer/vip/business/admin/super_admin，默认为customer
        type: String,
        // customer: 普通用户, vip: VIP用户, business: 企业用户, admin: 管理员, super_admin: 超级管理员
        enum: ['customer', 'vip', 'business', 'admin', 'super_admin'], 
        default: 'customer'
    },
    userLevel: { // 用户等级，0-10级，默认为0
        type: Number,
        min: [0, "用户等级不能低于0"],
        max: [10, "用户等级不能高于10"],
        default: 0
    },
    points: { // 积分，不能为负数，默认为0
        type: Number,
        min: [0, "积分不能为负数"],
        default: 0
    },
    balance: { // 余额，不能为负数，默认为0
        type: Number,
        min: [0, "余额不能为负数"],
        default: 0
    },
    totalSpent: { // 总消费金额，不能为负数，默认为0
        type: Number,
        min: [0, "总消费不能为负数"],
        default: 0
    },
    totalOrders: { // 总订单数，不能为负数，默认为0
        type: Number,
        min: [0, "总订单数不能为负数"],
        default: 0
    },
    description : { // 个人描述，最大100个字符
        type: String,
        maxlength : [100,"最多100个字符!"]
    },
    status : { // 用户状态，支持active/inactive/banned，默认为active
        type: String,
        enum: ['active', 'inactive', 'banned'], // active: 正常活跃状态, inactive: 非活跃状态, banned: 被封禁状态
        default: 'active'
    },
    isDeleted: { // 软删除标记，默认为false，用于逻辑删除
        type: Boolean,
        default: false,
        index: true
    },
    deletedAt: { // 删除时间，记录用户被删除的时间
        type: Date
    },
    registeredIp: { // 注册IP地址，记录用户注册时的IP
        type: String,
        trim: true
    },
    lastLoginIp: { // 最后登录IP，记录用户最后登录的IP
        type: String,
        trim: true
    },
    lastLoginAt : { // 最后登录时间，记录用户最后登录时间
        type: Date
    },
    createAt : { // 创建时间，默认为当前时间
        type: Date,
        default: Date.now
    },
    updateAt : { // 更新时间，记录最后修改时间
        type: Date
    }
})

/**
 * 密码加密中间件
 * 在用户保存前自动加密密码和生成用户ID
 * @param {Function} next - 下一个中间件函数
 * @description 生成自动增长的用户ID，仅在密码字段被修改时执行加密操作，使用bcrypt进行10轮盐值加密
 */
schema.pre('save', async function(next) {
    // 如果是新用户，生成自动增长的userId
    if (this.isNew && !this.userId) {
        try {
            const counter = await UserIdCounter.findByIdAndUpdate(
                'userId',
                { $inc: { sequence_value: 1 } },
                { new: true, upsert: true }
            );
            this.userId = counter.sequence_value;
        } catch (error) {
            return next(error);
        }
    }
    
    // 如果密码被修改，进行加密
    if (this.isModified('password')) {
        try {
            const salt = await bcrypt.genSalt(10);
            this.password = await bcrypt.hash(this.password, salt);
            this.updateAt = new Date();
        } catch (error) {
            return next(error);
        }
    }
    
    next();
});

/**
 * 检查字段值是否唯一
 * @param {string} field - 要检查的字段名
 * @param {string} value - 要检查的值
 * @param {string} [excludeId=null] - 排除的用户ID（用于更新时排除自身）
 * @returns {Promise<boolean>} - 如果值唯一返回true，否则返回false
 * @description 用于验证邮箱、手机号等字段的唯一性，支持排除指定用户ID（更新操作时使用）
 */
schema.statics.isFieldUnique = async function(field, value , excludeId = null) {
    const query = {[field]: value};

    if (excludeId) {
        query._id = {$ne: excludeId};
    }
    const ex = await this.findOne(query);
    return ex;//bool
}

/**
 * 根据用户ID查找用户
 * @param {number} userId - 独立的用户ID
 * @returns {Promise<Object|null>} - 返回用户对象，如果未找到返回null
 * @description 通过独立的自动增长用户ID查询用户信息
 */
schema.statics.findByUserId = async function(userId) {
    return await this.findOne({ userId: userId });
}

/**
 * 根据用户名查询用户信息
 * @param {string} username - 用户名
 * @returns {Promise<Object|null>} - 返回用户对象，如果未找到返回null
 * @description 通过用户名查询用户信息
 */
schema.statics.findByUsername = async function(username) {
    try {
        return await this.findOne({ username: username, isDeleted: false });
    } catch (error) {
        console.error('通过用户名查询用户失败:', error);
        return null;
    }
};

/**
 * 根据邮箱查找用户
 * @param {string} email - 用户邮箱地址
 * @returns {Promise<Object|null>} - 返回用户对象，如果未找到返回null
 * @description 通过邮箱地址查询用户信息，常用于登录验证
 */
schema.statics.findByEmail = async function(email) {
    return await this.findOne({ email: email });
}

/**
 * 验证用户密码
 * @param {string} email - 用户邮箱地址
 * @param {string} password - 明文密码
 * @returns {Promise<Object|null>} - 验证成功返回用户对象，失败返回null
 * @description 通过邮箱查找用户并验证密码，用于用户登录验证
 */
schema.statics.validatePassword = async function(email, password) {
    const user = await this.findByEmail(email);
    if (!user) return null;
    
    const isValid = await bcrypt.compare(password, user.password);
    return isValid ? user : null;
}

/**
 * 创建新用户
 * @param {Object} userData - 用户数据对象
 * @param {string} userData.email - 用户邮箱
 * @param {string} userData.password - 用户密码
 * @param {string} [userData.phone] - 用户手机号（可选）
 * @param {string} [userData.description] - 用户描述（可选）
 * @param {number} [userData.age] - 用户年龄（可选）
 * @param {string} [userData.avatar] - 用户头像路径（可选）
 * @returns {Promise<Object>} - 返回包含成功状态和用户信息的对象
 * @description 创建新用户，密码会自动加密，包含数据验证和错误处理
 */
schema.statics.createUser = async function(userData) {
    try {
        const user = new this(userData);
        await user.save();
        return { success: true, user: user };
    } catch (error) {
        return { success: false, error: error.message };
    }
}

/**
 * 更新用户最后登录时间
 * @param {string} userId - 用户ID（MongoDB ObjectId）
 * @returns {Promise<Object|null>} - 返回更新后的用户对象，如果未找到返回null
 * @description 记录用户最后登录时间，用于登录统计和用户活跃度分析
 */
schema.statics.updateLastLogin = async function(userId) {
    return await this.findByIdAndUpdate(
        userId, 
        { lastLoginAt: new Date() }, 
        { new: true }
    );
}

/**
 * 封禁用户
 * @param {string} userId - 用户ID（MongoDB ObjectId）
 * @returns {Promise<Object|null>} - 返回更新后的用户对象，如果未找到返回null
 * @description 将用户状态设置为'banned'，禁止用户登录和访问系统资源
 */
schema.statics.banUser = async function(userId) {
    return await this.findByIdAndUpdate(
        userId, 
        { status: 'banned', updateAt: new Date() }, 
        { new: true }
    );
}

/**
 * 解封用户
 * @param {string} userId - 用户ID（MongoDB ObjectId）
 * @returns {Promise<Object|null>} - 返回更新后的用户对象，如果未找到返回null
 * @description 将用户状态恢复为'active'，允许用户重新登录和访问系统
 */
schema.statics.unbanUser = async function(userId) {
    return await this.findByIdAndUpdate(
        userId, 
        { status: 'active', updateAt: new Date() }, 
        { new: true }
    );
}

/**
 * 停用用户
 * @param {string} userId - 用户ID（MongoDB ObjectId）
 * @returns {Promise<Object|null>} - 返回更新后的用户对象，如果未找到返回null
 * @description 将用户状态设置为'inactive'，通常用于用户主动注销账户
 */
schema.statics.deactivateUser = async function(userId) {
    return await this.findByIdAndUpdate(
        userId, 
        { status: 'inactive', updateAt: new Date() }, 
        { new: true }
    );
}

/**
 * 获取安全的用户JSON数据（移除敏感信息）
 * @returns {Object} - 返回移除了密码等敏感信息的用户对象
 * @description 将用户对象转换为普通对象并删除密码字段，用于API响应时保护用户隐私
 */
schema.methods.isSafeJSON = function(){
    const user = this.toObject(); //转换成Object类型
    delete user.password;  //返回的数据删除密码
    return user;
}

/**
 * 更新用户个人信息
 * @param {Object} updateData - 要更新的用户数据
 * @param {string} [updateData.phone] - 手机号
 * @param {string} [updateData.description] - 个人描述
 * @param {string} [updateData.nickname] - 昵称
 * @param {string} [updateData.gender] - 性别
 * @param {Date} [updateData.birthday] - 生日
 * @param {string} [updateData.avatar] - 头像路径
 * @returns {Promise<Object>} - 返回更新后的用户对象
 * @description 只允许更新指定的安全字段，自动更新时间戳，用于用户个人资料修改
 */
schema.methods.updateProfile = async function(updateData) {
    const allowedFields = ['phone', 'description', 'nickname', 'gender', 'birthday', 'avatar'];
    const updates = {};
    
    Object.keys(updateData).forEach(key => {
        if (allowedFields.includes(key)) {
            updates[key] = updateData[key];
        }
    });
    
    if (Object.keys(updates).length > 0) {
        updates.updateAt = new Date();
        Object.assign(this, updates);
        await this.save({validateBeforeSave: false});
    }
    
    return this;
}

/**
 * 检查用户是否处于激活状态
 * @returns {boolean} - 如果用户状态为'active'返回true，否则返回false
 * @description 用于判断用户是否可以正常登录和使用系统功能
 */
schema.methods.isActive = function() {
    return this.status === 'active';
}

/**
 * 检查用户是否被封禁
 * @returns {boolean} - 如果用户状态为'banned'返回true，否则返回false
 * @description 用于判断用户是否被封禁，封禁用户无法登录系统
 */
schema.methods.isBanned = function() {
    return this.status === 'banned';
}

/**
 * 更改用户密码
 * @param {string} newPassword - 新密码（明文）
 * @returns {Promise<boolean>} - 返回true表示密码修改成功
 * @description 修改用户密码，密码会自动加密存储，用于用户主动修改密码或重置密码
 */
schema.methods.changePassword = async function(newPassword) {
    this.password = newPassword;
    await this.save();
    return true;
}

/**
 * 更改用户头像
 * @param {string} newFilePath - 新密码（明文）
 * @returns {Promise<boolean>} - 返回true表示头像修改成功
 * @description 修改用户头像
 */
schema.methods.changeAvatar = async function(newFilePath) {
    this.avatar = newFilePath;
    await this.save({validateBeforeSave: false});
    return true;
}



/**
 * 更新用户积分
 * @param {string} userId - 用户ID
 * @param {number} points - 要增加的积分（可以为负数）
 * @returns {Promise<Object|null>} 返回更新后的用户对象或null
 */
schema.statics.updateUserPoints = async function(userId, points) {
    try {
        return await this.findOneAndUpdate(
            { userId: userId, isDeleted: false },
            { 
                $inc: { points: points },
                updateAt: new Date()
            },
            { new: true }
        );
    } catch (error) {
        console.error('更新用户积分失败:', error);
        return null;
    }
};

/**
 * 软删除用户（标记删除而非物理删除）
 * @param {string} userId - 用户ID
 * @returns {Promise<Object|null>} 返回更新后的用户对象或null
 */
schema.statics.softDeleteUser = async function(userId) {
    try {
        return await this.findOneAndUpdate(
            { userId: userId, isDeleted: false },
            { 
                isDeleted: true,
                deletedAt: new Date(),
                status: 'inactive',
                updateAt: new Date()
            },
            { new: true }
        );
    } catch (error) {
        console.error('软删除用户失败:', error);
        return null;
    }
};

/**
 * @description 获取用户消费统计
 * @param {string} userId - 用户ID
 * @returns {Promise<Object|null>} 返回包含消费统计的对象或null
 */
schema.statics.getUserStats = async function(userId) {
    try {
        const user = await this.findOne({ userId: userId, isDeleted: false });
        if (!user) return null;
        
        return {
            userId: user.userId,
            username: user.username,
            totalSpent: user.totalSpent,
            totalOrders: user.totalOrders,
            points: user.points,
            balance: user.balance,
            userLevel: user.userLevel,
            userType: user.userType,
            averageOrderValue: user.totalOrders > 0 ? user.totalSpent / user.totalOrders : 0
        };
    } catch (error) {
        console.error('获取用户消费统计失败:', error);
        return null;
    }
};

/**
/**
 * @description 更新用户消费统计（订单完成后调用）
 * @param {string} userId - 用户ID
 * @param {number} orderAmount - 订单金额
 * @returns {Promise<Object|null>} 返回更新后的用户对象或null
 */
schema.statics.updateUserConsumption = async function(userId, orderAmount) {
    try {
        return await this.findOneAndUpdate(
            { userId: userId, isDeleted: false },
            { 
                $inc: { 
                    totalSpent: orderAmount,
                    totalOrders: 1
                },
                updateAt: new Date()
            },
            { new: true }
        );
    } catch (error) {
        console.error('更新用户消费统计失败:', error);
        return null;
    }
};

/**
 * @description 更新用户状态
 * @param {string} userId - 用户ID
 * @param {string} status - 新状态(active/inactive/banned)
 * @returns {Promise<Object|null>} 返回更新后的用户对象或null
 */
schema.statics.updateUserStatus = async function(userId, status) {
    try {
        return await this.findOneAndUpdate(
            { userId: userId, isDeleted: false },
            { 
                status: status,
                updateAt: new Date()
            },
            { new: true }
        );
    } catch (error) {
        console.error('更新用户状态失败:', error);
        return null;
    }
};


/**
 * 用户模型导出
 * 模型名称：userinfos（Mongoose会自动加上's'后缀，实际集合名为'userinfos'）
 * @type {mongoose.Model}
 * @description 电商后台管理系统用户模型
 * 新增特性：独立的自动增长userId字段，从1开始递增
 * 电商专用功能：用户等级、积分系统、余额管理、消费统计、软删除
 * 包含字段：
 *   - 基础信息：userId、username、email、password、phone、nickname、gender、birthday
 *   - 电商业务：userType、userLevel、points、balance、totalSpent、totalOrders
 *   - 安全审计：status、isDeleted、registeredIp、lastLoginIp
 *   - 时间戳：lastLoginAt、createAt、updateAt、deletedAt
 * 包含静态方法：isFieldUnique, findByUsername, findByUserId, findByEmail, validatePassword, createUser, updateLastLogin, banUser, unbanUser, deactivateUser, updateUserPoints, softDeleteUser, getUserStats, updateUserConsumption, updateUserStatus
 * 包含实例方法：isSafeJSON, updateProfile, isActive, isBanned, changePassword
 */
module.exports = mongoose.model("userinfos", schema)


